package vim.meet.util;

import java.io.File;
import java.util.BitSet;

public class URLEncodeUtil {
	
	 static BitSet encodedInPath;

	    static {
	        encodedInPath = new BitSet(256);

	        // Set the bits corresponding to characters that are encoded in the
	        // path component of a URI.

	        // These characters are reserved in the path segment as described in
	        // RFC2396 section 3.3.
	        encodedInPath.set('=');
	        encodedInPath.set(';');
	        encodedInPath.set('?');
	        encodedInPath.set('/');

	        // These characters are defined as excluded in RFC2396 section 2.4.3
	        // and must be escaped if they occur in the data part of a URI.
	        encodedInPath.set('#');
	        encodedInPath.set(' ');
	        encodedInPath.set('<');
	        encodedInPath.set('>');
	        encodedInPath.set('%');
	        encodedInPath.set('"');
	        encodedInPath.set('{');
	        encodedInPath.set('}');
	        encodedInPath.set('|');
	        encodedInPath.set('\\');
	        encodedInPath.set('^');
	        encodedInPath.set('[');
	        encodedInPath.set(']');
	        encodedInPath.set('`');

	        // US ASCII control characters 00-1F and 7F.
	        for (int i=0; i<32; i++)
	            encodedInPath.set(i);
	        encodedInPath.set(127);
	    }

	  /**
     * 
     * 对path中的所有字符编码
     */
    public static String encodePath(String path) {
	return encodePath(path, true);
    }
    
   /**
    * 
    * @param path
    * @param flag if false 使用文件分隔符,而不是编码
    * @return
    */
    public static String encodePath(String path, boolean flag) {
        char[] retCC = new char[path.length() * 2 + 16];
        int    retLen = 0;
        char[] pathCC = path.toCharArray();

	int n = path.length();
        for (int i=0; i<n; i++) {
            char c = pathCC[i];
            if ((!flag && c == '/') || (flag && c == File.separatorChar))
                retCC[retLen++] = '/';
            else {
                if (c <= 0x007F) {
                    if (c >= 'a' && c <= 'z' || 
                        c >= 'A' && c <= 'Z' ||
                        c >= '0' && c <= '9') {
                        retCC[retLen++] = c;
                    } else
                    if (encodedInPath.get(c))
                        retLen = escape(retCC, c, retLen);
                    else
                        retCC[retLen++] = c;
                } else if (c > 0x07FF) {
                    retLen = escape(retCC, (char)(0xE0 | ((c >> 12) & 0x0F)), retLen);
                    retLen = escape(retCC, (char)(0x80 | ((c >>  6) & 0x3F)), retLen);
                    retLen = escape(retCC, (char)(0x80 | ((c >>  0) & 0x3F)), retLen);
                } else {
                    retLen = escape(retCC, (char)(0xC0 | ((c >>  6) & 0x1F)), retLen);
                    retLen = escape(retCC, (char)(0x80 | ((c >>  0) & 0x3F)), retLen);
                }
            }
            //worst case scenario for character [0x7ff-] every single
            //character will be encoded into 9 characters.
            if (retLen + 9 > retCC.length) {
                int newLen = retCC.length * 2 + 16;
                if (newLen < 0) {
                    newLen = Integer.MAX_VALUE;
                }
                char[] buf = new char[newLen];
                System.arraycopy(retCC, 0, buf, 0, retLen);
                retCC = buf;
            }
        }
        return new String(retCC, 0, retLen);
    }

    
    private static int escape(char[] cc, char c, int index) {
        cc[index++] = '%';
        cc[index++] = Character.forDigit((c >> 4) & 0xF, 16);
        cc[index++] = Character.forDigit(c & 0xF, 16);
        return index;
    }

   
    private static char unescape(String s, int i) {
        return (char) Integer.parseInt(s.substring(i+1,i+3),16);
    }

    
    public static String decode(String s) {
        StringBuilder sb = new StringBuilder();

        int i=0;
        while (i<s.length()) {
            char c = s.charAt(i);
            char c2, c3;

            if (c != '%') {
                i++;
            } else {
                try {
                    c = unescape(s, i);
                    i += 3;

                    if ((c & 0x80) != 0) {
                        switch (c >> 4) {
                            case 0xC: case 0xD:
                                c2 = unescape(s, i);
                                i += 3;
                                c = (char)(((c & 0x1f) << 6) | (c2 & 0x3f));
                                break;

                            case 0xE:
                                c2 = unescape(s, i);
                                i += 3;
                                c3 = unescape(s, i);
                                i += 3;
                                c = (char)(((c & 0x0f) << 12) |
                                           ((c2 & 0x3f) << 6) |
                                            (c3 & 0x3f));
                                break;

                            default:
                                throw new IllegalArgumentException();
                        }
                    }
                } catch (NumberFormatException e) {
                    throw new IllegalArgumentException();
                }
            }

            sb.append(c);
        }

        return sb.toString();
    }

}
